package net.orange.architecture.web;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;

import net.orange.architecture.db.Conditions;
import net.orange.architecture.db.ListConditions;
import net.orange.architecture.model.BaseValueObject;
import net.orange.architecture.process.IBaseProcess;
import net.orange.architecture.process.ProcessFactory;

/**
 * Base web controller which is the JSF manage bean. it process the web user
 * action and forward the specify page.
 */
public abstract class BaseController {

	/**
	 * The data model of the data when it is a listing page.
	 */
	protected DataModel datas = new ListDataModel();

	/**
	 * The data model of the data when it is a editing page.
	 */
	protected BaseValueObject content;

	/**
	 * The edit status, true for writable , false for read only.
	 */
	protected boolean editMode = false;

	/**
	 * The list conditions and information (like total page count, current page
	 * number) when it is a listing page.
	 */
	protected ListConditions listConditions;

	/**
	 * Return the target object Class, it need to implement this method in every
	 * child class, the system will initialize the business logical object and
	 * DAO object according the class automaticlly.
	 * 
	 * @return The target object class.
	 */
	protected abstract Class getClazz();

	/**
	 * Retrieves the objects according the list conditions and show up the list
	 * page
	 * 
	 * @return The JSF form outcome. "search" to forward to listing page,throw
	 *         exception else.
	 * @throws Exception
	 */
	public String search() throws Exception {
		ListConditions lc = getListConditions();

		lc.setCurrPage(1);
		datas.setWrappedData(null);
		return Web.JSF_NAVIGATION_SEARCH;
	}

	/**
	 * Go to the first page.
	 * 
	 * @return
	 * @throws Exception
	 */
	public String firstPage() throws Exception {
		ListConditions lc = getListConditions();

		lc.setCurrPage(1);
		datas.setWrappedData(null);
		return Web.JSF_NAVIGATION_SEARCH;
	}

	/**
	 * Go to the previous page.
	 * 
	 * @return
	 * @throws Exception
	 */
	public String previousPage() throws Exception {
		ListConditions lc = getListConditions();

		int page = (lc.getCurrPage() > 1) ? lc.getCurrPage() - 1 : 1;
		lc.setCurrPage(page);
		datas.setWrappedData(null);
		return Web.JSF_NAVIGATION_SEARCH;
	}

	/**
	 * Go to the next page.
	 * 
	 * @return
	 * @throws Exception
	 */
	public String nextPage() throws Exception {
		ListConditions lc = getListConditions();

		int page = (lc.getCurrPage() < lc.getPageCount()) ? lc.getCurrPage() + 1
				: lc.getPageCount();
		lc.setCurrPage(page);
		datas.setWrappedData(null);
		return Web.JSF_NAVIGATION_SEARCH;
	}

	/**
	 * Go to the last page.
	 * 
	 * @return
	 * @throws Exception
	 */
	public String lastPage() throws Exception {
		ListConditions lc = getListConditions();

		lc.setCurrPage(lc.getPageCount());
		datas.setWrappedData(null);
		return Web.JSF_NAVIGATION_SEARCH;
	}

	/**
	 * Open the selected object in editing page(read only status).
	 * 
	 * @return The JSF form outcome. "open" to forward to editing page,throw
	 *         exception else.
	 * @throws Exception
	 */
	public String open() throws Exception {
		content = getRowData();
		IBaseProcess process = ProcessFactory.load(getClazz());
		content = process.find(getRowData().getPk());

		this.editMode = false;
		return Web.JSF_NAVIGATION_OPEN;
	}

	/**
	 * Initialize an new object in editing page.
	 * 
	 * @return The JSF form outcome. "new" to forward to editing page,throw
	 *         exception else.
	 * @throws Exception
	 */
	public String create() throws Exception {
		content = (BaseValueObject) getClazz().getConstructor().newInstance();
		this.editMode = true;
		return Web.JSF_NAVIGATION_NEW;
	}

	/**
	 * Copy an existed object as a new object.
	 * 
	 * @return The JSF form outcome. "copy" to forward to editing page,throw
	 *         exception else.
	 * @throws Exception
	 */
	public String copy() throws Exception {

		content = (BaseValueObject) getRowData().clone();
		content.setPk(0);

		this.editMode = true;

		return Web.JSF_NAVIGATION_OPEN;
	}

	/**
	 * delete the selected object in listing page.
	 * 
	 * @return The JSF form outcome. "delete" to forward to listing page,throw
	 *         exception else.
	 * @throws Exception
	 */
	public String delete() throws Exception {
		BaseValueObject row = (BaseValueObject) datas.getRowData();
		IBaseProcess process = ProcessFactory.load(getClazz());
		Collection<String> messages = new ArrayList<String>();
		if (!process.checkDelete(row, messages)) {

			Web.setMessage(null, formatErrorMessages(messages));
			return Web.JSF_NAVIGATION_DELETE;
		}
		process.delete(row);
		datas.setWrappedData(null);
		return Web.JSF_NAVIGATION_DELETE;
	}

	/**
	 * Open the selected object in editing page(writable status).
	 * 
	 * @return The JSF form outcome. "edit" to forward to editing page,throw
	 *         exception else.
	 * @throws Exception
	 */
	public String edit() throws Exception {
		this.editMode = true;
		
		long npk = content.getPk();
		IBaseProcess process = ProcessFactory.load(getClazz());
		content = process.find(npk);
		
		return Web.JSF_NAVIGATION_EDIT;

	}

	/**
	 * Save the object change in in editing page and set the page back to read
	 * only status.
	 * 
	 * @return The JSF form outcome. "retry" to forward to editing page,throw
	 *         exception else.
	 * @throws Exception
	 */
	public String save() throws Exception {
		Collection<String> messages = new ArrayList<String>();
		IBaseProcess process = ProcessFactory.load(getClazz());
		if (!process.checkSave(content, (content.getPk() <= 0), messages)) {
			Web.setMessage(null, formatErrorMessages(messages));
			return Web.JSF_NAVIGATION_RETRY;
		}
		if (content.getPk() > 0) {
			process.update(content);
		} else {
			process.insert(content);
		}
		editMode = false;
		return Web.JSF_NAVIGATION_SAVE;
	}

	/**
	 * Forward the editing page to listing page.
	 * 
	 * @return The JSF form outcome. "back" to forward to editing page,throw
	 *         exception else.
	 * @throws Exception
	 */
	public String back() throws Exception {
		datas.setWrappedData(null);
		return Web.JSF_NAVIGATION_BACK;
	}

	/**
	 * Construct the list conditions from the web page. include the search bean
	 * paremeter, search conditions.
	 * 
	 * @param lc
	 *            The target list conditions.
	 * @throws Exception
	 */
	public void loadConditions(ListConditions lc) throws Exception {
		Map map = Web.getRequestParameterMap();

		Conditions cds = Conditions.toConditions(map);

		lc.clear();
		lc.add(cds);
	}

	/**
	 * @return the listCondition
	 */
	public ListConditions getListConditions() {
		if (listConditions == null)
			listConditions = new ListConditions();

		return listConditions;
	}

	/**
	 * @param listCondition
	 *            the listCondition to set
	 */
	public void setListCondition(ListConditions listConditions) {
		this.listConditions = listConditions;
	}

	/**
	 * @return the editMode
	 */
	public boolean isEditMode() {
		return editMode;
	}

	/**
	 * @param editMode
	 *            the editMode to set
	 */
	public void setEditMode(boolean editMode) {
		this.editMode = editMode;
	}

	/**
	 * @return the datas
	 * @throws Exception
	 */
	public DataModel getDatas() throws Exception {
		if (datas != null && datas.getWrappedData() != null)
			return datas;

		IBaseProcess process = ProcessFactory.load(getClazz());
		loadConditions(getListConditions());
		datas.setWrappedData(process.list(getListConditions()));
		return datas;
	}

	/**
	 * @param datas
	 *            the datas to set
	 */
	public void setDatas(DataModel datas) {
		this.datas = datas;
	}

	/**
	 * @return the content
	 */
	public BaseValueObject getContent() throws Exception {
		return content;
	}

	/**
	 * @param content
	 *            the content to set
	 */
	public void setContent(BaseValueObject content) {
		this.content = content;
	}

	/**
	 * Format the error message form collection type to string.
	 * 
	 * @param c
	 *            The error message in collection.
	 * @return The message in string format.
	 */
	private String formatErrorMessages(Collection c) {
		String s = "";

		if (c != null) {
			for (Object element : c)
				s += "* " + element + "\n";
		}
		return s;
	}

	/**
	 * Return the page labe
	 * 
	 * @return The page lable
	 */
	public String getPageLabel() {
		int currPage = getListConditions().getCurrPage();
		int pageCount = getListConditions().getPageCount();
		return String.valueOf(currPage) + "/" + String.valueOf(pageCount);
	}

	/**
	 * Return if it is the first page.
	 * 
	 * @return if it is the first page.
	 */
	public boolean isfirstPage() {
		ListConditions lc = getListConditions();

		return (lc.getCurrPage() <= 1);
	}

	/**
	 * Return if it is the last page.
	 * 
	 * @return if it is the last page.
	 */
	public boolean isLastPage() {
		ListConditions lc = getListConditions();

		return (lc.getCurrPage() >= lc.getPageCount());
	}

	/**
	 * Get the current the data object of the current row.
	 * 
	 * @return The current the data object of the current row.
	 */
	private BaseValueObject getRowData() {
		return ((datas) != null) ? (BaseValueObject) datas.getRowData() : null;

	}
}
